﻿using System;
using System.Collections.Generic;
using System.Linq;

using FireBus.Contracts;
using FireBus.Shell.Contracts;


namespace FireBus.Shell.ExtensionManager
{
	public sealed class Extension :
		IRequestListener<CommandRequest, CommandResponse>,
		IRequestListener<CommandListRequest, CommandListResponse>,
		IRequestSender<ExtensionListRequest, ExtensionListResponse>,
		IRequestSender<ExtensionStateRequest, ExtensionStateResponse>,
		IEventNotifier<ExtensionLoadEvent>,
		IEventNotifier<ExtensionUnloadEvent>
	{


		private readonly IDictionary<String, ICommand> _Commands
			= new Dictionary<String, ICommand>();


		public Extension()
		{
			var types = typeof (Extension).Assembly.GetTypes();
			foreach (var type in types)
			{
				if (!type.GetInterfaces().Contains(typeof (ICommand)))
				{
					continue;
				}
				var constructor = type.GetConstructor(
					new[]
						{
							typeof (Extension)
						});
				var command = (ICommand) constructor.Invoke(
					new[] {this});
				_Commands.Add(
					command.Name, command);
			}
		}

		public CommandResponse OnRequest(CommandRequest request)
		{
			if (request == null)
			{
				throw new ArgumentNullException("request");
			}
			String output = null;
			ICommand command;
			if (_Commands.TryGetValue(request.Command, out command))
			{
				output = command.Execute(request.Arguments);
			}
			return new CommandResponse(output);
		}

		public CommandListResponse OnRequest(CommandListRequest request)
		{
			var commands =
				_Commands.Select(pair => pair.Value).Select(
					command => new ShellCommandToken(command.Name, command.Description)).ToList();
			return new CommandListResponse(
				commands);
		}

		public IEnumerable<ExtensionToken> GetExtensions()
		{
			if (_SendExtensionListRequest != null)
			{
				var args =
					new SendEventArgs<ExtensionListRequest, ExtensionListResponse>(
						new ExtensionListRequest(),
						false);
				_SendExtensionListRequest(this, args);
				var response = args.Responses.First;
				if (response != null)
				{
					return response.Extensions;
				}
			}
			return new ExtensionToken[0];
		}

		private SendEventHandler<ExtensionListRequest, ExtensionListResponse> _SendExtensionListRequest;

		event SendEventHandler<ExtensionListRequest, ExtensionListResponse> IRequestSender<ExtensionListRequest, ExtensionListResponse>.Send
		{
			add { _SendExtensionListRequest += value; }
			remove { _SendExtensionListRequest -= value; }
		}

		public ExtensionState GetExtensionState(ExtensionName extensionName)
		{
			if (_SendExtensionStateRequest != null)
			{
				var args =
				new SendEventArgs<ExtensionStateRequest, ExtensionStateResponse>(
					new ExtensionStateRequest(extensionName), false);
				_SendExtensionStateRequest(this, args);
				var response = args.Responses.First;
				if (response != null)
				{
					return response.State;
				}
			}
			return ExtensionState.NotAvailable;
		}

		private SendEventHandler<ExtensionStateRequest, ExtensionStateResponse> _SendExtensionStateRequest;

		event SendEventHandler<ExtensionStateRequest, ExtensionStateResponse> IRequestSender<ExtensionStateRequest, ExtensionStateResponse>.Send
		{
			add { _SendExtensionStateRequest += value; }
			remove { _SendExtensionStateRequest -= value; }
		}

		public void LoadExtension(ExtensionName extensionName)
		{
			if (_NotifyExtensionLoad == null)
			{
				return;
			}
			_NotifyExtensionLoad(
				this,
				new NotifyEventArgs<ExtensionLoadEvent>(
					new ExtensionLoadEvent(
						extensionName)));
		}

		private NotifyEventHandler<ExtensionLoadEvent> _NotifyExtensionLoad;

		event NotifyEventHandler<ExtensionLoadEvent> IEventNotifier<ExtensionLoadEvent>.Notify
		{
			add { _NotifyExtensionLoad += value; }
			remove { _NotifyExtensionLoad -= value; }
		}

		public void UnloadExtension(ExtensionName extensionName)
		{
			if (_NotifyExtensionUnload == null)
			{
				return;
			}
			_NotifyExtensionUnload(
				this,
				new NotifyEventArgs<ExtensionUnloadEvent>(
					new ExtensionUnloadEvent(
						extensionName)));
		}

		private NotifyEventHandler<ExtensionUnloadEvent> _NotifyExtensionUnload;

		event NotifyEventHandler<ExtensionUnloadEvent> IEventNotifier<ExtensionUnloadEvent>.Notify
		{
			add { _NotifyExtensionUnload += value; }
			remove { _NotifyExtensionUnload -= value; }
		}
	}
}
